class Container (object):
	"self.Children is a tuple that contains all the classes that can be children of this class"

	Token = None
	Children = ()

	def __init__ (self, Filename, LineNo):
		"self.ChildrenTokens is a dict with children's tokens mapped to children's classes"
		self.Filename = Filename
		self.LineNo = LineNo
		self.Current = None
		self.ChildrenTokens = dict ((Child.Token, Child) for Child in self.Children)
		self.Data = {}

	def __str__ (self):
		Token = [self.Token,]
		Children = [self.Data.get (Child.Token) for Child in self.Children]
		return ' '.join (Token + [str (Child) for Child in Children if Child is not None])

	def token (self, Filename, LineNo, Token, RawToken=None):
		"first try to pass the token deeper to the current child, then try to make a new child with this token"
		if self.Current is not None and self.Current.token (Filename, LineNo, Token, RawToken):
			return True
		if Token in self.ChildrenTokens:
			self.commit ()
			self.Current = self.ChildrenTokens [Token] (Filename, LineNo)
			return True
		return False

	def commitCurrent (self):
		if hasattr (self.Current, 'commit') and callable (self.Current.commit):
			self.Current.commit ()
		if hasattr (self.Current, 'check') and callable (self.Current.check):
			self.Current.check ()

	def commit (self):
		if self.Current is None:
			return None
		Token = self.Current.Token
		if self.Data.get (Token) is not None:
			self.fail_check ("Multiple '%s' instruction detected" % Token)
		self.commitCurrent ()
		self.Data [Token] = self.Current
		self.Current = None

	def child (self, ChildClass):
		if not hasattr (ChildClass, 'Token'):
			return False
		return self.Data.get (ChildClass.Token)

	def fail_token (self, Filename, LineNo, Message):
		from Exceptions import mTokenError
		raise mTokenError (Message, Filename, LineNo)

	def fail_check (self, Message):
		from Exceptions import mCheckError
		raise mCheckError (Message, self.Filename, self.LineNo)
# ----------------------------------------------------------------------------

class Argument (object):
	AllowMultiple = True
	AllowInvert = True
	RawTokens = False
	AllowEmpty = False

	def __init__ (self, Filename, LineNo):
		self.Filename = Filename
		self.LineNo = LineNo
		self.Invert = False
		self.Values = []
		self.State = 'INIT'

	def __str__ (self):
		Result = [self.Token]
		if self.Invert:
			Result.append ('not')
		if len (self.Values) > 1:
			Result.append ('{')
			Result.extend (self.escape (Value) for Value in self.Values)
			Result.append ('}')
		else:
			Result.extend (self.escape (Value) for Value in self.Values)
		return ' '.join (Result)

	def token (self, Filename, LineNo, Token, RawToken=None):
		if self.State == 'FIN':
			return False
		elif self.State == 'INIT':
			if Token in ('not', '!'):
				if self.AllowInvert:
					self.Invert = not self.Invert
				else:
					self.fail_token (Filename, LineNo, "'%s' argument can not be negated" % self.Token)
			elif Token == '{':
				if self.AllowMultiple:
					self.State = 'READ'
				else:
					self.fail_token (Filename, LineNo, "'%s' argument can not accept a list" % self.Token)
			else:
				if self.RawTokens:
					self.Values.append (RawToken)
				else:
					self.Values.append (Token)
				self.State = 'FIN'
		elif self.State == 'READ':
			if Token == '}':
				self.State = 'FIN'
			else:
				if self.RawTokens:
					self.Values.append (RawToken)
				else:
					self.Values.append (Token)
		return True

	@staticmethod
	def escape (Value):
		Value = Value.replace ('"', '\\"')
		if len (Value.split ()) > 1:
			Value = '"%s"' % Value
		return Value

	def check (self):
		if not self.AllowEmpty and not self.Values:
			self.fail_check ("'%s' argument can not be left empty" % self.Token)
		for Value in self.Values:
			self.check_value (Value)

	def check_value (self, Value):
		return True

	def fail_token (self, Filename, LineNo, Message):
		from Exceptions import mTokenError
		raise mTokenError (Message, Filename, LineNo)

	def fail_check (self, Message):
		from Exceptions import mCheckError
		raise mCheckError (Message, self.Filename, self.LineNo)
# ----------------------------------------------------------------------------

class Instruction (Container):
	pass
# ----------------------------------------------------------------------------

class Action (Instruction):
	def render (self):
		raise NotImplementedError
# ----------------------------------------------------------------------------
